"use strict";

var C = require('../common'),
    path = require('path'),
    NodeAuth = require('../nodeauth');

var AuthManager = function () {
    this.modules = {};
    this.mountActions = {};
};

AuthManager.prototype.mountController = function (methodPath, controllerAction, preferedMethod) {
    if (methodPath.indexOf(':') === -1) {
        if (preferedMethod) {
            preferedMethod = preferedMethod.toLowerCase();
        } else {
            preferedMethod = 'get';
        }

        methodPath = preferedMethod + ':' + methodPath;
    }

    this.mountActions[methodPath] = controllerAction;
};

AuthManager.prototype.matchAction = function (methodPath, router) {
    var action = this.mountActions[methodPath];

    if (action) {
        action(router);
        return true;
    }

    return false;
};

AuthManager.prototype.loadModules = function (webModule, route, options, cb) {
    var self = this;

    this.names = C._.keys(options);

    C.async.eachSeries(this.names, function (authName, cb2) {
        var moduleOptions = options[authName];

        var moduleName = moduleOptions.module;
        moduleName || C.throwRequired('auth[' + authName + '].module');

        var authModule = webModule.loadExtension('auth_modules', moduleName);
        var controllerFile = webModule.toAbsPath(path.join('auth', authName) + '.js');

        var meta = {
            controllerName: authName,
            controller: require(controllerFile),
            options: moduleOptions.options,
            route: route
        };

        self.modules[authName] = {
            module: authModule,
            meta: meta
        };

        webModule.console('Setting up auth module "' + authName + '" ...');

        authModule.setup(self, webModule, meta, function (err) {
            if (err) return cb2(err);

            webModule.console('Auth module "' + authName + '" is loaded.');
            cb2();
        });
    }, cb);
};

AuthManager.prototype.initInstances = function (router, cb) {

    var self = this;

    C.async.eachSeries(this.names, function (name, cb2) {

        var authModule = self.createInstance(router, name);
        authModule.init(cb2);
    }, cb);
};

AuthManager.prototype.createInstance = function (router, name) {
    var moduleInfo = this.modules[name];
    var AuthModule = moduleInfo.module;
    var authModule = new AuthModule(router, moduleInfo.meta);

    router.auth || (router.auth = {});
    router.auth[name] = authModule;
    return authModule;
};

module.exports = function (webModule, route, options, cb) {

    var manager = new AuthManager();

    manager.loadModules(webModule, route, options, function (err) {
        if (err) return cb(err);

        cb(null, function (req, res, next) {
            var router = req.router;
            if (router.auth) {
                throw new C.Error.UsageError('"auth" already exists!');
            }

            //dispatch if matched
            var actionKey = req.method.toLowerCase() + ':' + req.path;

            if (manager.matchAction(actionKey, router)) {
                return;
            }

            //read all auth data
            manager.initInstances(router, next);
        });
    });
};